import time
from functools import wraps
from longguo_ui.utils.logger import logger


class RetryUtils:
    """
    失败重试工具类，提供装饰器和手动重试两种方式
    支持配置最大重试次数、初始延迟时间和延迟倍数（指数退避策略）
    用于处理临时网络波动、资源竞争等导致的偶发性失败
    """

    def __init__(self, max_retries=3, delay=1, backoff=2):
        """
        初始化重试参数

        :param max_retries: 最大重试次数，默认为3次
        :param delay: 初始延迟时间（秒），默认为1秒
        :param backoff: 延迟倍数（指数退避），每次重试后延迟时间 = 当前延迟 * backoff，默认为2
        """
        self.max_retries = max_retries  # 最大重试次数
        self.delay = delay  # 初始延迟时间(秒)
        self.backoff = backoff  # 延迟倍数(指数退避)

    def retry(self, exceptions=(Exception,)):
        """
        重试装饰器，用于包装可能偶发失败的函数，自动进行重试

        :param exceptions: 需要捕获并重试的异常类型元组，默认为捕获所有Exception类型
        :return: 装饰器函数
        """

        def decorator(func):
            """装饰器内层函数，用于接收被装饰的函数"""

            @wraps(func)  # 保留被装饰函数的元信息（如名称、文档字符串）
            def wrapper(*args, **kwargs):
                """
                包装函数，实现重试逻辑

                :param args: 被装饰函数的位置参数
                :param kwargs: 被装饰函数的关键字参数
                :return: 被装饰函数的返回值
                :raises: 当达到最大重试次数后仍失败时，抛出最后一次捕获的异常
                """
                retries = 0  # 当前重试次数计数器
                current_delay = self.delay  # 当前延迟时间，初始化为初始延迟

                # 循环执行重试，直到达到最大重试次数
                while retries <= self.max_retries:
                    try:
                        # 尝试执行目标函数，成功则直接返回结果
                        return func(*args, **kwargs)
                    except exceptions as e:
                        # 捕获指定类型的异常，准备重试
                        retries += 1  # 重试次数加1

                        # 若达到最大重试次数，记录错误并抛出异常
                        if retries > self.max_retries:
                            logger.error(f"达到最大重试次数({self.max_retries})，调用失败: {str(e)}")
                            raise

                        # 记录警告信息，包含重试次数、延迟时间和异常详情
                        logger.warning(
                            f"调用失败，将进行第{retries}次重试(总次数{self.max_retries})，"
                            f"延迟{current_delay}秒: {str(e)}"
                        )
                        # 等待当前延迟时间后再重试
                        time.sleep(current_delay)
                        # 计算下一次重试的延迟时间（指数退避）
                        current_delay *= self.backoff

            return wrapper  # 返回包装函数

        return decorator  # 返回装饰器函数

    def execute_with_retry(self, func, *args, exceptions=(Exception,), **kwargs):
        """
        手动执行带重试的函数，适用于不适合使用装饰器的场景

        :param func: 要执行的目标函数
        :param args: 目标函数的位置参数
        :param exceptions: 需要捕获并重试的异常类型元组，默认为捕获所有Exception类型
        :param kwargs: 目标函数的关键字参数
        :return: 目标函数的返回值
        :raises: 当达到最大重试次数后仍失败时，抛出最后一次捕获的异常
        """
        retries = 0  # 当前重试次数计数器
        current_delay = self.delay  # 当前延迟时间，初始化为初始延迟

        # 循环执行重试，直到达到最大重试次数
        while retries <= self.max_retries:
            try:
                # 尝试执行目标函数，成功则直接返回结果
                return func(*args, **kwargs)
            except exceptions as e:
                # 捕获指定类型的异常，准备重试
                retries += 1  # 重试次数加1

                # 若达到最大重试次数，记录错误并抛出异常
                if retries > self.max_retries:
                    logger.error(f"达到最大重试次数({self.max_retries})，执行失败: {str(e)}")
                    raise

                # 记录警告信息，包含重试次数、延迟时间和异常详情
                logger.warning(
                    f"执行失败，将进行第{retries}次重试(总次数{self.max_retries})，"
                    f"延迟{current_delay}秒: {str(e)}"
                )
                # 等待当前延迟时间后再重试
                time.sleep(current_delay)
                # 计算下一次重试的延迟时间（指数退避）
                current_delay *= self.backoff

        # 理论上不会到达此位置，若到达则抛出异常
        raise Exception("重试逻辑异常退出")